﻿using Elasticsearch.Net;
using Microsoft.Extensions.Configuration;
using Nest;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Threading.Tasks;

namespace ElasticSearchDemo.Service
{

    /// <summary>
    /// 访问ElasticSearch服务类
    /// </summary>
    public class ESSever : IESServer
    {

        private string url = $"http://47.96.130.80:9200/_xpack/sql?format=csv";

        /// <summary>
        /// Linq查询的官方Client
        /// </summary>
        public IElasticClient ElasticLinqClient { get; set; }


        /// <summary>
        /// Js查询的官方Client
        /// </summary>
        public IElasticLowLevelClient ElasticJsonClient { get; set; }


        private readonly IConfiguration _configuration;



        public ESSever(IConfiguration configuration)
        {
            _configuration = configuration;

            //配置节点地址,以,分开
            List<Uri> uris = _configuration["ElasticSearchConfig:Url"].Split(",").ToList().ConvertAll(x => new Uri(x));
            //配置请求池
            StaticConnectionPool connectionPool = new StaticConnectionPool(uris);
            //请求配置参数
            var settings = new ConnectionSettings(connectionPool)
                .BasicAuthentication(_configuration["ElasticSearchConfig:ESUser"], _configuration["ElasticSearchConfig:ESPwd"])
                .RequestTimeout(TimeSpan.FromSeconds(30));
            //Linq请求客户端初始化
            this.ElasticLinqClient = new ElasticClient(settings);
            //Json请求客户端初始化
            this.ElasticJsonClient = new ElasticLowLevelClient(settings);
        }

        /// <summary>
        /// Add Document
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        /// <param name="indexName"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<CreateResponse> AddAsync<TEntity>(TEntity entity, string indexName, string id) where TEntity : class
        {
            CreateResponse response = await ElasticLinqClient.CreateAsync<TEntity>(entity, t => t.Index(indexName).Id(id));

            return response;
        }


        /// <summary>
        /// Add Range Doucment
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        /// <param name="indexName"></param>
        /// <returns></returns>
        public async Task<BulkResponse> AddRangeAsync<TEntity>(IEnumerable<TEntity> entity, string indexName) where TEntity : class
        {
            BulkResponse response = await ElasticLinqClient.IndexManyAsync(entity, indexName);

            return response;
        }

    


        /// <summary>
        /// Update Document
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        /// <param name="indexName"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<UpdateResponse<TEntity>> UpdateAsync<TEntity>(TEntity entity, string indexName, string id) where TEntity : class
        {
            UpdateResponse<TEntity> response = await ElasticLinqClient.UpdateAsync<TEntity>(id, t => t.Index(indexName).Doc(entity));

            return response;
        }

        /// <summary>
        /// Delete Document
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="entity"></param>
        /// <param name="indexName"></param>
        /// <returns></returns>
        public async Task<DeleteResponse> DeleteAsync(string indexName, string id)
        {
            DeleteRequest deleteRequest = new DeleteRequest(indexName, id);

            DeleteResponse response = await ElasticLinqClient.DeleteAsync(deleteRequest);

            return response;
        }

        /// <summary>
        /// Get Document
        /// </summary>
        /// <typeparam name="TEntity"></typeparam>
        /// <param name="indexName"></param>
        /// <param name="id"></param>
        /// <returns></returns>
        public async Task<GetResponse<TEntity>> GetAsync<TEntity>(string indexName, string id) where TEntity : class
        {
            DocumentPath<TEntity> path = new DocumentPath<TEntity>(id);

            GetResponse<TEntity> response = await ElasticLinqClient.GetAsync<TEntity>(path, s => s.Index(indexName));

            return response;
        }

        /// <summary>
        /// 判断索引是否存在
        /// </summary>
        /// <param name="indexName"></param>
        /// <param name="selector"></param>
        /// <returns></returns>
        public async Task<bool> ExistsIndex(string indexName, Func<IndexExistsDescriptor, IIndexExistsRequest> selector = null)
        {

            ExistsResponse existsResponse = await ElasticLinqClient.Indices.ExistsAsync(indexName, selector);

            return existsResponse.Exists;
        }

        /// <summary>
        /// 创建索引
        /// </summary>
        /// <param name="indexName"></param>
        /// <param name="numberOfShards"></param>
        /// <param name="numberOfReplicas"></param>
        /// <returns></returns>
        public async Task<CreateIndexResponse> CreateIndex(string indexName, int numberOfShards = 5, int numberOfReplicas = 1)
        {
            IIndexState indexState = new IndexState
            {
                Settings = new IndexSettings
                {
                    NumberOfShards = numberOfShards,//分片数量
                    NumberOfReplicas = numberOfReplicas//副本数量
                }
            };

            Func<CreateIndexDescriptor, ICreateIndexRequest> func = x => x.InitializeUsing(indexState).Map(m => m.AutoMap());
            CreateIndexResponse response = await ElasticLinqClient.Indices.CreateAsync(indexName, func);
            return response;
        }


        /// <summary>
        /// 删除索引
        /// </summary>
        /// <param name="elasticClient"></param>
        /// <param name="indexName"></param>
        public async Task<DeleteIndexResponse> DeleteIndexAsync(string indexName)
        {
            DeleteIndexResponse response = await ElasticLinqClient.Indices.DeleteAsync(indexName);
            return response;
        }

        /// <summary>
        /// 根据SQL获取数据
        /// </summary>
        /// <param name="sql"></param>
        /// <returns></returns>
      	public DataTable GetDataBySql(string sql)
        {
            DataTable dataTable = new DataTable();
            try
            {
                string jsonstr = Post(url, new QueryParam() { query = sql });
                var lines = jsonstr.Split("\r\n");
                foreach (string item in lines[0].Split(","))
                {
                    //可以做类型转换
                    dataTable.Columns.Add(item, typeof(System.String));
                }
                for (int i = 1; i < lines.Length - 1; i++)
                {
                    var filedvalue = lines[i].Split(",");
                    var row = dataTable.NewRow();
                    for (int j = 0; j < dataTable.Columns.Count; j++)
                    {
                        row[j] = filedvalue[j];
                    }
                    dataTable.Rows.Add(row);
                }
            }
            catch (Exception ex)
            {
                throw;
            }
            return dataTable;


        }



        private string Post(string url, QueryParam queryParam)
        {
            HttpWebRequest request = null;
            try
            {
                request = (HttpWebRequest)WebRequest.Create(url);
                var data = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(queryParam));
                //request.Accept = "application/json; charset=UTF-8"; // 设置响应数据的ContentType
                request.Method = "POST";
                request.ContentType = "application/json"; // 设置请求数据的ContentType
                request.ContentLength = data.Length;
                request.Timeout = 90000;
                request.Credentials = CredentialCache.DefaultCredentials;
                //获得用户名密码的Base64编码
                string code = Convert.ToBase64String(Encoding.ASCII.GetBytes(string.Format("{0}:{1}", _configuration["ElasticSearchConfig:ESUser"], _configuration["ElasticSearchConfig:ESPwd"])));

                //添加Authorization到HTTP头
                request.Headers.Add("Authorization", "Basic " + code);

                // 设置入参
                using (var stream = request.GetRequestStream())
                {
                    stream.Write(data, 0, data.Length);
                }
                // 发送请求
                var response = (HttpWebResponse)request.GetResponse();
                // 读取出参
                using (var resStream = response.GetResponseStream())
                {
                    using (var reader = new StreamReader(resStream, Encoding.UTF8))
                    {
                        return reader.ReadToEnd();
                    }
                }
            }
            catch (Exception ex)
            {
                return null;
            }
            finally
            {
                // 释放连接
                if (request != null) request.Abort();
            }
        }

    }


    public class QueryParam
    {
        public string query { get; set; }
    }


}
